package org.kjtc.emoji;
import java.io.UnsupportedEncodingException;

import java.util.Collections;

import java.util.List;



/**

 * This class represents an emoji.<br>

 * <br>

 * This object is immutable so it can be used safely in a multithreaded context.

 *

 * @author xiaojinlu

 */

public class Emoji {

    private final String description;

    private final boolean supportsFitzpatrick;

    private final List<String> aliases;

    private final List<String> tags;

    private final String unicode;

    private final String htmlDec;

    private final String htmlHex;



    /**

     * Constructor for the Emoji.

     *

     * @param description         The description of the emoji

     * @param supportsFitzpatrick Whether the emoji supports Fitzpatrick modifiers

     * @param aliases             the aliases for this emoji

     * @param tags                the tags associated with this emoji

     * @param bytes               the bytes that represent the emoji

     */

    protected Emoji(

            String description,

            boolean supportsFitzpatrick,

            List<String> aliases,

            List<String> tags,

            byte... bytes

    ) {

        this.description = description;

        this.supportsFitzpatrick = supportsFitzpatrick;

        this.aliases = Collections.unmodifiableList(aliases);

        this.tags = Collections.unmodifiableList(tags);



        int count = 0;

        try {

            this.unicode = new String(bytes, "UTF-8");

            int stringLength = getUnicode().length();

            String[] pointCodes = new String[stringLength];

            String[] pointCodesHex = new String[stringLength];



            for (int offset = 0; offset < stringLength; ) {

                final int codePoint = getUnicode().codePointAt(offset);



                pointCodes[count] = String.format("&#%d;", codePoint);

                pointCodesHex[count++] = String.format("&#x%x;", codePoint);



                offset += Character.charCount(codePoint);

            }

            this.htmlDec = stringJoin(pointCodes, count);

            this.htmlHex = stringJoin(pointCodesHex, count);

        } catch (UnsupportedEncodingException e) {

            throw new RuntimeException(e);

        }

    }



    /**

     * Method to replace String.join, since it was only introduced in java8

     * @param array the array to be concatenated

     * @return concatenated String

     */

    private String stringJoin(String[] array, int count){

        String joined = "";

        for(int i = 0; i < count; i++)

            joined += array[i];

        return joined;

    }



    /**

     * Returns the description of the emoji

     *

     * @return the description

     */

    public String getDescription() {

        return this.description;

    }



    /**

     * Returns wether the emoji supports the Fitzpatrick modifiers or not

     *

     * @return true if the emoji supports the Fitzpatrick modifiers

     */

    public boolean supportsFitzpatrick() {

        return this.supportsFitzpatrick;

    }



    /**

     * Returns the aliases of the emoji

     *

     * @return the aliases (unmodifiable)

     */

    public List<String> getAliases() {

        return this.aliases;

    }



    /**

     * Returns the tags of the emoji

     *

     * @return the tags (unmodifiable)

     */

    public List<String> getTags() {

        return this.tags;

    }



    /**

     * Returns the unicode representation of the emoji

     *

     * @return the unicode representation

     */

    public String getUnicode() {

        return this.unicode;

    }



    /**

     * Returns the unicode representation of the emoji associated with the

     * provided Fitzpatrick modifier.<br>

     * If the modifier is null, then the result is similar to

     * {@link Emoji#getUnicode()}

     *

     * @param fitzpatrick the fitzpatrick modifier or null

     *

     * @return the unicode representation

     * @throws UnsupportedOperationException if the emoji doesn't support the

     * Fitzpatrick modifiers

     */

    public String getUnicode(Fitzpatrick fitzpatrick) {

        if (!this.supportsFitzpatrick()) {

            throw new UnsupportedOperationException(

                    "Cannot get the unicode with a fitzpatrick modifier, " +

                            "the emoji doesn't support fitzpatrick."

            );

        } else if (fitzpatrick == null) {

            return this.getUnicode();

        }

        return this.getUnicode() + fitzpatrick.unicode;

    }



    /**

     * Returns the HTML decimal representation of the emoji

     *

     * @return the HTML decimal representation

     */

    public String getHtmlDecimal() {

        return this.htmlDec;

    }



    /**

     * @deprecated identical to {@link #getHtmlHexadecimal()} for

     * backwards-compatibility. Use that instead.

     *

     * @return the HTML hexadecimal representation

     */

    public String getHtmlHexidecimal() {

        return this.getHtmlHexadecimal();

    }



    /**

     * Returns the HTML hexadecimal representation of the emoji

     *

     * @return the HTML hexadecimal representation

     */

    public String getHtmlHexadecimal() {

        return this.htmlHex;

    }



    @Override

    public boolean equals(Object other) {

        return !(other == null || !(other instanceof Emoji)) &&

                ((Emoji) other).getUnicode().equals(getUnicode());

    }



    @Override

    public int hashCode() {

        return unicode.hashCode();

    }



    /**

     * Returns the String representation of the Emoji object.<br>

     * <br>

     * Example:<br>

     * <code>Emoji {

     *   description='smiling face with open mouth and smiling eyes',

     *   supportsFitzpatrick=false,

     *   aliases=[smile],

     *   tags=[happy, joy, pleased],

     *   unicode='😄',

     *   htmlDec='&amp;#128516;',

     *   htmlHex='&amp;#x1f604;'

     * }</code>

     *

     * @return the string representation

     */

    @Override

    public String toString() {

        return "Emoji{" +

                "description='" + description + '\'' +

                ", supportsFitzpatrick=" + supportsFitzpatrick +

                ", aliases=" + aliases +

                ", tags=" + tags +

                ", unicode='" + unicode + '\'' +

                ", htmlDec='" + htmlDec + '\'' +

                ", htmlHex='" + htmlHex + '\'' +

                '}';

    }

}